import { app } from "../../../base/typings/global";
import { ByteView } from "../byte/ByteView";
import { IProtocolHelper, ProtocolID, HeadDecoder, HeadEncoder, Protocol } from "../NetInterface";
import { SocketLogType } from "./SocketNode";

let handlerMap:Map<number, Function> = new Map();

/**
 * socket response 处理装饰器
 * @param id 协议号
 * @returns 
 */
export function cmd(id:number){
    return function (target:any, propertyKey:string){
        handlerMap[id] = (buffer:ByteView)=>{
            return target[propertyKey](buffer);
        }
    }
}

/**
 * 基础协议处理器，实现返回数据后的decode操作。
 * 可以通过继承BaseProtocolHelper，自定义decode逻辑、心跳协议等
 */
export class BaseProtocolHelper implements IProtocolHelper {
    protected _heartbeatID:ProtocolID = 0;
    protected _parsers: Map<number, Function> = new Map();
    protected _headDecoder:HeadDecoder = null;
    protected _headEncoder:HeadEncoder = null;

    public constructor() {
        this._parsers = handlerMap;
    }

    /**
     * 初始化协议处理器
     * @param heartbeat 心跳协议ID 取值范围 0-65535，注意避免与其他协议冲突
     * @param headDecoder 协议头解析函数 类型：HeadDecoder，默认length(Uint16)、ProtocolID(Uint16)
     * @param heartbeatID 心跳协议号，默认0
     */
    public init(headDecoder?:HeadDecoder, headEncoder?:HeadEncoder, heartbeatID?:number):void {
        this._heartbeatID = heartbeatID || 0;
        this._headDecoder = headDecoder;
        this._headEncoder = headEncoder;
        // parsers.forEach((value, key)=>{
        //     this._parsers.set(key, value);
        // })
    }

    public decode(buffer: ByteView):Protocol {
        let protocol:Protocol = this.decodeHead(buffer);
        if (this._parsers.has(protocol.id)) {
            protocol.data = this._parsers.get(protocol.id)(buffer);
            app.net.socket.log(SocketLogType.S2C, protocol);
        }else {
            app.net.socket.log(SocketLogType.S2xC, protocol);
        }
        
        return protocol;
    }

    public heartbeat():Protocol {
        this.flush(this._heartbeatID);
        return {id: this._heartbeatID, len:0, data:null};
    }

    protected decodeHead(buffer: ByteView):Protocol {
        if(this._headDecoder){
            return this._headDecoder(buffer);
        }
        //default head decode
        let length = buffer.readUint16();
        let id:ProtocolID = buffer.readUint16();
        return {id: id, len:length, data: null};
    }

    protected flush(id:ProtocolID, params?:IArguments):boolean {
        let length = this.output.pos || 4;
        if(this._headEncoder){
            this._headEncoder(this.output, id, length);
        }else{  // default head encode
            this.output.pos = 0;
            this.output.writeInt16(length - 4);
            this.output.writeInt16(id);
        }
        let result = app.net.socket.flush();
        if(result){
            app.net.socket.log(SocketLogType.C2S, {id:id, len:length, data:params||null})
        }
        return result
    }

    protected get output():ByteView {
        return app.net.socket.output;
    }
}